#!/bin/sh

unset arch cross target cc ar strip tp cflags uselds

uselds=yes   # use custom ld script if available for the target

# Configurable global paths for config.h

BASE_ETC="/base/etc"     # config files
BASE_VAR="/var/base"     # saved state
INIT_ETC="/etc"          # config files in initrd mode
RUN_CTRL="/run/ctrl"     # control sockets

HERE=""  # gets prepended to misc global paths, HERE "/run" -> "./run"

# In development mode, make configurable tools read configs from current
# (source) directory, not the host files that might be present.

devel_paths()
{
	HERE=`pwd`
	BASE_ETC="$HERE/etc"
	INIT_ETC="$HERE/etc"
	BASE_VAR="$HERE/var"
	RUN_CTRL="$HERE/var/run"
}

optim=-Os

# Argument parsing

die() { echo "$@" >&2; exit 1; }
accept() { eval "$key=\$val"; unset val; }
setkey() { eval "$1=\$val"; unset val; }
append() { eval "$1=\"\${$1:+\$$1 }\$2\""; }
appval() { append "$1" "$val"; unset val; }

while [ $# -gt 0 ]; do
	key=${1%%=*}
	val=${1#*=}
	test "$key" = "$1" && unset val

	case "$key" in
		cross)  accept; cross="${cross}-" ;;
		target) accept; cross="${cross:-$target-}" ;;
		arch)   accept ;;
		cc|CC)  setkey cc ;;
		ar|AR)  setkey ar ;;
		strip)  setkey strip ;;
		STRIP)  setkey strip ;;
		cflags) appval cflags; unset optim ;;
		CFLAGS) appval cflags; unset optim ;;
		devel)  append cflags "-Wall -g"; unset optim; devel_paths ;;
		debug)  append cflags "-Wall -g" ;;
		wextra) append cflags "-Wextra" ;;
		werror) append cflags "-Werror" ;;
		qemu)   accept ;;
		BASE_ETC) accept ;;
		BASE_VAR) accept ;;
		INIT_ETC) accept ;;
		RUN_CTRL) accept ;;
		nolds) unset uselds ;;
		*) die "Unexpected argument $key" ;;
	esac; shift

	test -n "$val" && die "Unexpected argument for $key"
done

# Pick and configure toolchain based on the supplied arguments

append cflags "$optim"

test -z "$ar" && ar="${cross}ar"
test -z "$strip" && strip="${cross}strip"

case "$cc" in
	*clang*) die "clang is not supported" ;;
esac

test -z "$cc" && cc="${cross}gcc"

if [ -z "$arch" ]; then
	mach=`$cc -dumpmachine`
	mach=${mach%%-*}
	case "$mach" in
		i[3456]86) arch=i386 ;;
		mips64el)  arch=mips64 ;;
		mipsn32)   arch=mips32 ;;
		mipsn32el) arch=mips32 ;;
		mipsel)    arch=mips ;;
		armv*)     arch=arm ;;
		*) arch="$mach"
	esac
	test -n "$arch" || die "Cannot determine target arch"
	test -d "lib/arch/$arch" || die "Unsupported arch $arch"
fi

# Default meaning of qemu=something is to use qemu-something,
# however if it's qemu=/path/to/qemu-blah then use that unchanged.
if [ -z "$qemu" ]; then
	host=`uname -m`
	if [ "$host" = "$mach" ]; then
		qemu="-"
	elif [ "$arch" = "i386" ]; then
		qemu="$arch"
	else
		qemu="$mach"
	fi
fi

case "$qemu" in
	-) qemu='' ;;
	*-*) ;;
	*/*) ;;
	*) qemu="qemu-$qemu" ;;
esac

# Write the resulting configuration (config.h and config.mk)

cat > config.mk <<END
ARCH = $arch

CC = \$/mini-cc
AR = $ar
LD = \$(CC)
AS = \$(CC)
STRIP = $strip
QEMU = $qemu

.SUFFIXES:

%.o: %.c
	\$(CC) -c $<

%.o: %.s
	\$(CC) -c $<

MAKEFLAGS = -R
END

cat > mini-cc <<END
#!/bin/sh

case "\$0" in
	*/*) base="\${0%/*}" ;;
	*) echo "\$0 may not be called from PATH" >&2; exit 1 ;;
esac

needslib=yes

for _ in "\$@"; do
	case "\$_" in
		-c|-E|-S) unset needslib ;;
	esac
done

set $cc -ffreestanding -fomit-frame-pointer \\
	-fno-asynchronous-unwind-tables -fno-stack-protector \\
	-static -fno-pie -fno-PIE -fno-pic -fno-PIC \\
	-nostdinc -I"\$base"/lib/arch/$arch -I"\$base"/lib -MD \\
	$cflags "\$@"

END
# ^above: GCC may be configured to produce PIEs without explicit -pie options,
# and it would do so even if given -ffreestanding -static. This is not
# acceptable for minibase, so the code below tries to force non-PIE mode.

# Below: use custom linker script if available, otherwise link with lib/all.a
# and libgcc.a; note it must be a linker group because reasons.

ldscript="lib/arch/$arch/static.ld"

if [ -n "$uselds" -a -f "$ldscript" ]; then
cat >> mini-cc <<END
test -n "\$needslib" && \
	set "\$@" -nostdlib \\
		-Wl,-\\( "\$base"/lib.a -lgcc -Wl,-\\) \\
		-T "\$base"/$ldscript

exec "\$@"
END
else
cat >> mini-cc <<END
test -n "\$needslib" && \
	set "\$@" -nostdlib \\
		-Wl,-\\( "\$base"/lib.a -lgcc -Wl,-\\)

exec "\$@"
END
fi

chmod a+x mini-cc

cat > config.h <<END
#define BASE_ETC "$BASE_ETC"
#define BASE_VAR "$BASE_VAR"
#define INIT_ETC "$INIT_ETC"
#define RUN_CTRL "$RUN_CTRL"

#define HERE "$HERE"
END

# And finally dump the values for the user to check

echo "Configured for $arch"
echo
echo "  CC = $cc"
echo "  AR = $ar"
echo "  STRIP = $strip"
echo "  CFLAGS = $cflags"
test -n "$qemu" && echo "  QEMU = $qemu"
echo
echo "  BASE_ETC = $BASE_ETC"
echo "  BASE_VAR = $BASE_VAR"
echo "  INIT_ETC = $INIT_ETC"
echo "  RUN_CTRL = $RUN_CTRL"
echo
echo "Proceed to run 'make'"
